Skip to main content

symmetry

Some block cipher modes, such as OFB, CTR, or CFB, turn a block cipher into a stream cipher. The idea behind stream ciphers is to produce a pseudorandom keystream which is then XORed with the plaintext. One advantage of stream ciphers is that they can work of plaintext of arbitrary length, with no padding required.

OFB is an obscure cipher mode, with no real benefits these days over using CTR. This challenge introduces an unusual property of OFB.

source.py
from Crypto.Cipher import AES
import os,requests

KEY = ""
FLAG = ""

@chal.route('/symmetry/encrypt/<plaintext>/<iv>/')
def encrypt(plaintext, iv):
plaintext = bytes.fromhex(plaintext)
iv = bytes.fromhex(iv)
if len(iv) != 16:
return {"error": "IV length must be 16"}

cipher = AES.new(KEY, AES.MODE_OFB, iv)
encrypted = cipher.encrypt(plaintext)
ciphertext = encrypted.hex()
return {"ciphertext": ciphertext}

@chal.route('/symmetry/encrypt_flag/')
def encrypt_flag():
iv = os.urandom(16)

cipher = AES.new(KEY, AES.MODE_OFB, iv)
encrypted = cipher.encrypt(FLAG.encode())
ciphertext = iv.hex() + encrypted.hex()
return {"ciphertext": ciphertext}

Solution:

This WriteUp Solution is password protected by the flag of the challenge.

As we can see the image below we can get the plaintext from ciphertext by simply encrypting it again becuase the plaintext is not begin encrypted with the key but with the IV itself.

loading

So find IV from encrypt_flag and encrypt the ciphertext with the IV to get the flag.

solve.py
from Crypto.Cipher import AES
import os,requests

def encrypt(plaintext,iv):
r=requests.get('https://aes.cryptohack.org/symmetry/encrypt/'+plaintext+'/'+iv)
return r.json()['ciphertext']

r=requests.get('https://aes.cryptohack.org/symmetry/encrypt_flag/')
c = r.json()['ciphertext']
iv = c[:32]
cipher = c[32:]
flag = bytes.fromhex(encrypt(cipher,iv)).decode()
print(flag)

After running the script, we get the flag as crypto{0fb_15_5ymm37r1c4l_!!!11!}